﻿using Anley.DomainDrivenDesign.Contract.Configuration;
using Anley.DomainDrivenDesign.Contract.Extension;

namespace Anley.DomainDrivenDesign.Contract.Helper;

/// <summary>
/// 加密帮助类
/// </summary>
public class EncryptionHelper
    : IEncryptionHelper
{
    private const string defaultSM2ECName = "sm2p256v1";
    private const string defaultSM4Algorithm = "SM4/ECB/PKCS7Padding";
    private readonly EncryptionConfiguration _encryptionConfiguration;

    /// <summary>
    /// 构造函数
    /// </summary>
    public EncryptionHelper(
        IOptionsMonitor<EncryptionConfiguration> encryptionConfiguration)
    {
        _encryptionConfiguration = encryptionConfiguration.CurrentValue;
    }

    #region 国密算法

    #region SM2加密/解密

    // SM2服务端加密
    public string SM2ServerEncrypt(
        string plainText,
        string? publicKey = null)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            return string.Empty;
        if (string.IsNullOrWhiteSpace(publicKey))
            publicKey = _encryptionConfiguration
                .SM2Server.NotNull(nameof(EncryptionConfiguration.SM2Server))
                .PublicKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM2Server.PublicKey));
        return SM2Encrypt(
            plainText,
            publicKey);
    }

    // SM2客户端加密
    public string SM2ClientEncrypt(
        string plainText,
        string? publicKey = null)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            return string.Empty;
        if (string.IsNullOrWhiteSpace(publicKey))
            publicKey = _encryptionConfiguration
                .SM2Client.NotNull(nameof(EncryptionConfiguration.SM2Client))
                .PublicKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM2Client.PublicKey));
        return SM2Encrypt(
            plainText,
            publicKey);
    }

    // SM2加密
    private string SM2Encrypt(
        string plainText,
        string publicKey)
    {
        // 初始化加密程序
        var x9ec = ECNamedCurveTable.GetByName(defaultSM2ECName);
        var publicKeyParameters = new ParametersWithRandom(
                new ECPublicKeyParameters(
                    x9ec.Curve.DecodePoint(publicKey.HexStringToBytes()),
                    new ECDomainParameters(x9ec)));
        var sm2 = new SM2Engine(SM2Engine.Mode.C1C3C2);
        sm2.Init(true, publicKeyParameters);
        // 加密
        var plainData = Encoding.UTF8.GetBytes(plainText);
        var cipherData = sm2.ProcessBlock(plainData, 0, plainData.Length);
        var cipherText = cipherData.BytesToHexString();
        if (cipherText.StartsWith("04")) // 如果04开头，需要去除，前端才能解密
            cipherText = cipherText.Substring(2);
        // 返回结果
        return cipherText;
    }

    // SM2服务端解密
    public string SM2ServerDecrypt(
        string cipherText,
        string? privateKey = null)
    {
        if (string.IsNullOrWhiteSpace(cipherText))
            return string.Empty;
        if (string.IsNullOrWhiteSpace(privateKey))
            privateKey = _encryptionConfiguration
                .SM2Server.NotNull(nameof(EncryptionConfiguration.SM2Server))
                .PrivateKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM2Server.PrivateKey));
        // 初始化解密程序
        var x9ec = ECNamedCurveTable.GetByName(defaultSM2ECName);
        var privateKeyParameters = new ECPrivateKeyParameters(
            new BigInteger(1, privateKey.HexStringToBytes()),
            new ECDomainParameters(x9ec));
        var sm2 = new SM2Engine(SM2Engine.Mode.C1C3C2);
        sm2.Init(false, privateKeyParameters);
        // 解密
        if (!cipherText.StartsWith("04")) // 如果不是04开头，需要加上，后端才能解密
            cipherText = $"04{cipherText}";
        var cipherData = cipherText.HexStringToBytes();
        var plainData = sm2.ProcessBlock(cipherData, 0, cipherData.Length);
        // 返回结果
        return Encoding.UTF8.GetString(plainData);
    }

    #endregion

    #region SM2签名/验签

    // SM2服务端签名
    public string SM2ServerSignature(
        string plainText,
        string? privateKey = null)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            throw new ArgumentNullException(nameof(plainText));
        if (string.IsNullOrWhiteSpace(privateKey))
            privateKey = _encryptionConfiguration
                .SM2Server.NotNull(nameof(EncryptionConfiguration.SM2Server))
                .PrivateKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM2Server.PrivateKey));
        // 初始化签名程序
        var x9ec = ECNamedCurveTable.GetByName(defaultSM2ECName);
        var privateKeyParameters = new ECPrivateKeyParameters(
           new BigInteger(1, privateKey.HexStringToBytes()),
           new ECDomainParameters(x9ec));
        var sm2 = new SM2Signer();
        sm2.Init(true, new ParametersWithRandom(privateKeyParameters));
        // 签名
        var plainData = Encoding.UTF8.GetBytes(plainText);
        sm2.BlockUpdate(plainData, 0, plainData.Length);
        var signData = sm2.GenerateSignature();
        // 返回结果
        return signData.BytesToHexString();
    }

    // SM2服务端验签
    public bool SM2ServerVerifySignature(
        string plainText,
        string signText,
        string? publicKey = null)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            throw new ArgumentNullException(nameof(plainText));
        if (string.IsNullOrWhiteSpace(signText))
            throw new ArgumentNullException(nameof(signText));
        if (string.IsNullOrWhiteSpace(publicKey))
            publicKey = _encryptionConfiguration
                .SM2Server.NotNull(nameof(EncryptionConfiguration.SM2Server))
                .PublicKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM2Server.PublicKey));
        return SM2VerifySignature(
            plainText,
            signText,
            publicKey);
    }

    // SM2客户端验签
    public bool SM2ClientVerifySignature(
        string plainText,
        string signText,
        string? publicKey = null)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            throw new ArgumentNullException(nameof(plainText));
        if (string.IsNullOrWhiteSpace(signText))
            throw new ArgumentNullException(nameof(signText));
        if (string.IsNullOrWhiteSpace(publicKey))
            publicKey = _encryptionConfiguration
                .SM2Client.NotNull(nameof(EncryptionConfiguration.SM2Client))
                .PublicKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM2Client.PublicKey));
        return SM2VerifySignature(
            plainText,
            signText,
            publicKey);
    }

    // SM2验签
    public bool SM2VerifySignature(
        string plainText,
        string signText,
        string publicKey)
    {
        // 初始化验签程序
        var x9ec = ECNamedCurveTable.GetByName(defaultSM2ECName);
        var keyParameters = new ECPublicKeyParameters(
                x9ec.Curve.DecodePoint(publicKey.HexStringToBytes()),
                new ECDomainParameters(x9ec));
        var sm2 = new SM2Signer();
        sm2.Init(false, keyParameters);
        // 验签
        var plainData = Encoding.UTF8.GetBytes(plainText);
        sm2.BlockUpdate(plainData, 0, plainData.Length);
        var signData = signText.HexStringToBytes();
        // 返回结果
        return sm2.VerifySignature(signData);
    }

    #endregion

    #region SM3

    // SM3签名
    public string SM3Signature(
        string plainText)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            return string.Empty;
        // 初始化加密程序
        var sm3 = new SM3Digest();
        var plainData = Encoding.UTF8.GetBytes(plainText);
        sm3.BlockUpdate(plainData, 0, plainData.Length);
        // 加密
        var cipherData = new byte[sm3.GetDigestSize()];
        sm3.DoFinal(cipherData, 0);
        // 返回结果
        return cipherData.BytesToHexString();
    }

    // SM3签名
    public string HMacSM3Signature(
        string plainText,
        string? secretKey = null)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            return string.Empty;
        if (string.IsNullOrWhiteSpace(secretKey))
            secretKey = _encryptionConfiguration
                .SM3.NotNull(nameof(EncryptionConfiguration.SM3))
                .SecretKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM3.SecretKey));
        // 初始化加密程序
        var sm3 = new SM3Digest();
        var mac = new HMac(sm3);
        var keyParameter = new KeyParameter(secretKey.HexStringToBytes());
        mac.Init(keyParameter);
        var plainData = Encoding.UTF8.GetBytes(plainText);
        mac.BlockUpdate(plainData, 0, plainData.Length);
        // 加密
        var cipherData = new byte[mac.GetMacSize()];
        mac.DoFinal(cipherData, 0);
        // 返回结果
        return cipherData.BytesToHexString();
    }

    #endregion

    #region SM4

    // SM4加密
    public string SM4Encrypt(
        string plainText,
        string? secretKey = null)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            return string.Empty;
        if (string.IsNullOrWhiteSpace(secretKey))
            secretKey = _encryptionConfiguration
                .SM4.NotNull(nameof(EncryptionConfiguration.SM4))
                .SecretKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM4.SecretKey));
        // 初始化加密程序
        var keyParameter = ParameterUtilities.CreateKeyParameter("SM4", secretKey.HexStringToBytes());
        var sm4 = CipherUtilities.GetCipher(defaultSM4Algorithm);
        sm4.Init(true, keyParameter);
        // 加密
        var plainData = Encoding.UTF8.GetBytes(plainText);
        var cipherData = sm4.DoFinal(plainData);
        // 返回结果
        return cipherData.BytesToHexString();
    }

    // SM4解密
    public string SM4Decrypt(
        string cipherText,
        string? secretKey = null)
    {
        if (string.IsNullOrWhiteSpace(cipherText))
            return string.Empty;
        if (string.IsNullOrWhiteSpace(secretKey))
            secretKey = _encryptionConfiguration
                .SM4.NotNull(nameof(EncryptionConfiguration.SM4))
                .SecretKey.NotNullOrWhiteSpace(nameof(EncryptionConfiguration.SM4.SecretKey));
        // 初始化加密程序
        var keyParameter = ParameterUtilities.CreateKeyParameter("SM4", secretKey.HexStringToBytes());
        var sm4 = CipherUtilities.GetCipher(defaultSM4Algorithm);
        sm4.Init(false, keyParameter);
        // 解密
        var cipherData = cipherText.HexStringToBytes();
        var plainData = sm4.DoFinal(cipherData);
        // 返回结果
        return Encoding.UTF8.GetString(plainData);
    }

    #endregion

    #endregion

    #region 加密算法

    /// <summary>
    /// SHA1加密
    /// </summary>
    /// <param name="plainText"></param>
    /// <returns></returns>
    public string SHA1Encrypt(string plainText)
    {
        if (string.IsNullOrWhiteSpace(plainText))
            return string.Empty;
        using var sha1 = SHA1.Create();
        byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
        byte[] cipherBytes = sha1.ComputeHash(plainBytes);
        // 将字节数组转换为十六进制字符串
        var sb = new StringBuilder();
        for (int i = 0; i < cipherBytes.Length; i++)
            sb.Append(cipherBytes[i].ToString("X2"));
        // 返回加密结果
        return sb.ToString();
    }

    #endregion
}